/** * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2014 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.html5player.client; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.catrobat.html5player.client.common.Look; import org.catrobat.html5player.client.common.LookData; import org.catrobat.html5player.client.common.Sound; import org.catrobat.html5player.client.common.SoundInfo; import org.catrobat.html5player.client.scripts.BroadcastScript; import org.catrobat.html5player.client.scripts.Script; import org.catrobat.html5player.client.scripts.StartScript; import org.catrobat.html5player.client.scripts.WhenScript; import org.catrobat.html5player.client.threading.CatScheduler; import org.catrobat.html5player.client.threading.CatThread; import org.catrobat.html5player.client.threading.WaitCount; import com.google.gwt.canvas.client.Canvas; import com.google.gwt.canvas.dom.client.Context2d; import com.google.gwt.canvas.dom.client.ImageData; import com.google.gwt.core.client.JavaScriptException; import com.google.gwt.dom.client.ImageElement; import com.google.gwt.media.client.Audio; import com.google.gwt.user.client.ui.Image; public class Sprite { private final String name; private boolean isBackground = false; private double volume; private Look look; private Image currentLook = null; private Map<String, Look> looks; private Map<String, Sound> sounds; private Set<Script> scripts; public Sprite(String name) { this.name = name; this.isBackground = false; this.volume = 70; this.looks = new LinkedHashMap<String, Look>(); this.sounds = new LinkedHashMap<String, Sound>(); this.scripts = new LinkedHashSet<Script>(); look = new Look(Stage.getInstance().getStageMiddleX(), Stage.getInstance().getStageMiddleY(), true); } public String getName() { return this.name; } public boolean isBackground() { return isBackground; } public void setBackground(boolean background) { isBackground = background; } public Look getLook() { return look; } public void addLookData(LookData lookData) { if (lookData != null && !looks.containsKey(lookData.getName())) { Look look = new Look(0.0f, 0.0f, true); look.setLookData(lookData); looks.put(lookData.getName(), look); } } public Look getLook(String name) { return looks.get(name); } public List<Look> getLooks() { List<Look> looks = new ArrayList<Look>(); looks.addAll(this.looks.values()); return looks; } /** * */ public ArrayList<String> getLookDataNames() { return new ArrayList<String>(looks.keySet()); } /** * */ public ArrayList<LookData> getLookData() { ArrayList <Look> looksList = new ArrayList<Look>(looks.values()); ArrayList<LookData> lookDataList = new ArrayList<LookData>(); for(Look look : looksList) { LookData lookData = look.getLookData(); lookDataList.add(lookData); } return lookDataList; } /** * */ public LookData getLookDataByName(String lookName) { if(looks.containsKey(lookName)) { return looks.get(lookName).getLookData(); } return null; } public void addScript(Script script) { if (script != null && !scripts.contains(script)) { scripts.add(script); } } // unused, only in sprite test public Script getScript(String name) { for (Script script : scripts) { if (script.getName().equals(name)) { return script; } } return null; } /** * * @param position * @return script */ public Script getScript(int position){ ArrayList<Script> scriptsList = new ArrayList<Script>(scripts); if(position < getNumberOfScripts()) return scriptsList.get(position); else return null; } public int getScriptIndex(Script script){ ArrayList<Script> scriptsList = new ArrayList<Script>(scripts); int index = 0; for(Script s : scriptsList) { if(s.getId() == script.getId()) { return index; } index++; } return -1; } public void addSound(SoundInfo soundInfo) { if (soundInfo != null && !sounds.containsKey(soundInfo.getId())) { Sound sound = new Sound(); sound.setSoundInfo(soundInfo); this.sounds.put(soundInfo.getId(), sound); } } public Sound getSound(String id) { return sounds.get(id); } /** * */ public void showLook() { if (look == null) { return; } LookData lookData = look.getLookData(); if(lookData == null) { return; } currentLook = ImageHandler.get().getImage(lookData.getFilename()); if(currentLook == null) return; lookData.setWidth(currentLook.getWidth()); lookData.setHeight(currentLook.getHeight()); } public void drawSprite() { if(currentLook == null || !look.isVisible()) { CatrobatDebug.info("current look = " + currentLook + "| visible = " + look.isVisible()); return; } long start = System.currentTimeMillis(); CatrobatDebug.debug("drawSprite: " + this.name + " - customename: " + look.getLookData().getFilename()); LookData lookData = look.getLookData(); double size = look.getSize(); double width = (double)lookData.getWidth() * size; double height = (double)lookData.getHeight() * size; if(look.getBrightnessValue() == 1){ Scene.get().drawImageJSNI(currentLook, look.getXPosition(), look.getYPosition(), -width / 2, -height / 2, width, height, -look.getRotation(), // the MINUS is important because canvas positive rotation is clockwise look.getAlphaValue()); } else { Scene.get().drawImageBrightness(currentLook, look.getXPosition(), look.getYPosition(), -width /2, -height/2, width, height, -look.getRotation(), look.getAlphaValue(), look.getBrightnessValue()); } CatrobatDebug.debug("drawSprite-execution took " + (System.currentTimeMillis() - start + " ms")); CatrobatDebug.debug("z-Pos: " + look.getZPosition() + " : name: " + this.name); } public void run() { CatrobatDebug.debug("Sprite: " + this.name + ".run() - add startscripts"); CatrobatDebug.debug("Number of scripts: " + this.getNumberOfScripts()); List<Script> scriptList = new ArrayList<Script>(); scriptList.addAll(scripts); Collections.sort(scriptList); Stage.getInstance().setCurrentSprite(this); for (Script script : scriptList) { //only add StartScripts to the scheduler if(script instanceof StartScript) { // CatrobatDebug.debug("script is no WhenScript or BroadcastScript, Sprite: " + this.name); CatThread thread = new CatThread(this.getName() + script.getName(), script); CatScheduler.get().schedule(thread); } else if (script instanceof WhenScript) { //CatrobatDebug.debug("script '" + script.getName() + "' is a WhenScript or a BroadcastScript, Sprite: " + this.name); } // script.run(); } } public void startTapScripts() { CatrobatDebug.debug("<<< Sprite: " + this.name + " startTapScripts() >>>"); Stage.getInstance().setCurrentSprite(this); for (Script script : scripts) { if (script.getType().equals(WhenScript.SCRIPT_TYPE)) { WhenScript touchScript = (WhenScript) script; if(CatScheduler.get().getThread(script.getExecutor()) != null) { CatrobatDebug.debug("Already existing Thread with WhenScript: " + script.getName()); continue; } else { CatrobatDebug.info("Add WhenScript to Scheduler"); } if(touchScript.hasScriptFinished()) { touchScript.resetWhenScript(); } CatThread thread = new CatThread(this.getName() + script.getName(), touchScript); CatScheduler.get().schedule(thread); CatrobatDebug.info("script added to scheduler"); } } } public void startScriptBroadcast(BroadcastScript script) { CatThread thread = new CatThread(this.getName() + script.getName(), script); CatScheduler.get().schedule(thread); } public void startScriptBroadcastWait(BroadcastScript script, WaitCount signaler) { CatThread thread = new CatThread(this.getName() + script.getName(), script); thread.signalFinishedExecution(signaler); CatScheduler.get().schedule(thread); } /** * * @return number of scripts */ public int getNumberOfScripts() { return scripts.size(); } /** * * @param relativeX * @param relativeY * @return */ public boolean processOnTouch(int relativeX, int relativeY) { //Create temp canvas with the current look only to determine click (alpha) Canvas temp = Canvas.createIfSupported(); temp.setWidth(Scene.get().getCanvas().getCoordinateSpaceWidth()+ "px"); temp.setHeight(Scene.get().getCanvas().getCoordinateSpaceHeight()+ "px"); temp.setCoordinateSpaceWidth(Scene.get().getCanvas().getCoordinateSpaceWidth()); temp.setCoordinateSpaceHeight(Scene.get().getCanvas().getCoordinateSpaceHeight()); LookData lookData = look.getLookData(); if(lookData == null) return false; double size = look.getSize(); double width = (double)lookData.getWidth() * size; double height = (double)lookData.getHeight() * size; double x = -width / 2; double y = -height / 2; Context2d context = temp.getContext2d(); context.translate(look.getXPosition(), look.getYPosition()); context.rotate((-look.getRotation()) * Math.PI / 180); context.drawImage((ImageElement)currentLook.getElement().cast(), x, y, width, height); if(context.getImageData(relativeX, relativeY, 1, 1).getAlphaAt(0, 0) == 0){ return false; } if (currentLook == null || !look.isVisible()) return false; double xPosition = look.getXPosition(); double yPosition = look.getYPosition(); double newSize = look.getSize(); LookData newLookData = look.getLookData(); double widthHalf = ((double)newLookData.getWidth() * newSize) / 2; double heightHalf = ((double)newLookData.getHeight() * newSize) / 2; if( xPosition + widthHalf > relativeX && xPosition - widthHalf < relativeX && yPosition + heightHalf > relativeY && yPosition - heightHalf < relativeY) { CatrobatDebug.info("Sprite " + this.name + " got touched"); return true; } return false; } public double getVolume() { return volume; } public void setVolume(double newVolume) { this.volume = newVolume; } public void playSound(String id) { Sound sound = getSound(id); Audio audio = sound != null ? sound.getAudio() : null; if (audio != null) { audio.setVolume((double) volume / 100); audio.play(); CatrobatDebug.debug("playSound - getError: " + audio.getError() + "..."); } else { Stage.getInstance().log("no sound with id: " + id); } } public void stopSound() { for (Sound sound : sounds.values()) { Audio audio = sound.getAudio(); if (audio != null) { audio.pause(); try { //reset double initialTime = audio.getInitialTime(); audio.setCurrentTime(initialTime); } catch(JavaScriptException e) { CatrobatDebug.debug("JavaScriptException in stopSound of sprite: " + this.name); CatrobatDebug.debug("initialTime: " + audio.getInitialTime()); CatrobatDebug.debug(e.getMessage()); } } } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Sprite other = (Sprite) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }